/**
 * BSP 板级支持包
 * Copyright (c) 2021 深圳市智辰科技有限公司
 * All rights reserved.
 */
#include <string.h>
#include <stdio.h>
#include <math.h>
#include "driver_pmu.h"
#include "driver_gpio.h"
#include "driver_system.h"
#include "driver_exti.h"
#include "driver_i2s.h"
#include "driver_frspim.h"

#include "nonos.h"
#include "audio.h"
#include "qchar.h"

#define LOGTAG "AUDIO"


// Codec 寄存器上电初始值
static const uint8_t _init_regs[] = {
	0x35, 0x04, 0xFD, 0xF3, 0x00, 0x01, 0xC8, 0x0E, 0x42, 0x22, 0x00, 0x00, 0x03, 0x03, 0x11, 0xC0,
	0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0x3F, 0x55, 0x03, 0x0E, 0x17, 0x00, 0x00, 0x00, 0x45, 0xBA, 0x45,
	0xBA, 0x0B, 0x30, 0x02, 0x00, 0x00
};

// Codec 配置寄存器
// driver_codec.codec_init()
static const audio_reg_t _regs[] = {
    { 0x15, 0x7F },    //pd all
    { 0x0C, 0x1F },    //mic bias control
    { 0x0D, 0x03 },    //LMIX RMIX PD
    { 0x0E, 0x3C },    //ADC control
    { 0x0F, 0xD1 },
    { 0x12, 0xFF },    //pd
    { 0x13, 0xFF },    //mute
    { 0x14, 0xFF },    //pd
    { 0x15, 0x3F },    //pd all
    { 0x16, 0x00 },
    { 0x17, 0x07 },
    { 0x18, 0x9C },
    { 0x1A, 0x00 },
    { 0x00, 0xF5 },
    { 0x25, 0x00 },
    { 0x19, 0x0F },    //gain, 0x3F ==> noise is big;  0x1F ==> lower volume. result = stg2 * 4 + stg1 *10, 0x2A
    { 0x09, 0x02 },    //SLAVE_I2S
    { 0x40, 0x81 },    //CODEC_STEREO
	{ 0xFF, 0xFF }
};

// 音频功放开关
static FUNC_PA_Switch _funcPASwitch = NULL;

// 音频工作模式
audio_mode_t _audio_mode = AUDIO_MODE_NONE;
audio_mode_t audio_mode(void) { return _audio_mode; }

// 寄存器写
void audio_write(uint8_t addr, uint8_t data) { frspim_wr(FR_SPI_CODEC_CHAN, addr, 1, (uint32_t)data); }
// 寄存器读
uint8_t audio_read(uint8_t addr)             { return (uint8_t)frspim_rd(FR_SPI_CODEC_CHAN, addr, 1); }

void audio_hw_set_freq(audio_sample_rate_t rate) {
	uint8_t regv;
	switch(rate) {
		case AUDIO_SAMPLE_RATE_8000 : regv = 0x00; break;
		case AUDIO_SAMPLE_RATE_12000: regv = 0x11; break;
		case AUDIO_SAMPLE_RATE_16000: regv = 0x22; break;
		case AUDIO_SAMPLE_RATE_24000: regv = 0x33; break;
		case AUDIO_SAMPLE_RATE_48000: regv = 0x44; break;
		case AUDIO_SAMPLE_RATE_8021 : regv = 0x88; break;
		case AUDIO_SAMPLE_RATE_11026: regv = 0x99; break;
		case AUDIO_SAMPLE_RATE_22059: regv = 0xAA; break;
		case AUDIO_SAMPLE_RATE_44100: regv = 0xBB; break;
		default: regv = 0x00;
	}
	
    audio_write(0x0B, 0x00);
    audio_write(0x0A, regv);
    audio_write(0x0B, 0x03);
}

void audio_enable_adc(void) {
	// 0xE0 = 0b11100000: BIAS power on, MICBIAS power on, PGA stage2 inverter power on, PGA stage2 power on, PGA stage1 power on
    audio_write(0x15, audio_read(0x15) & 0xE0);
	// 0xBB = 0b10111011: PAG mute disable, ADC channel power on
    audio_write(0x12, audio_read(0x12) & 0xBB);
	// MIC bias control
	// 0x1F = 0b00000011: normal mode, Low analog voltage disable, MIC bias control disable, BIAS current control = default, The ramp up control for VMID = fast
    audio_write(0x0C, 0x03);
}

void audio_disable_adc(void) {
    audio_write(0x15, audio_read(0x15) | 0x1F);
    audio_write(0x12, audio_read(0x12) | 0x44);
}

void audio_enable_dac(void) {
    audio_write(0x15, audio_read(0x15) & (~BV(4))); // 必须打开 “BIAS power on” ！否则声音很小！
    audio_write(0x14, 0x00);
    audio_write(0x12, audio_read(0x12) & 0xC4);
    audio_write(0x13, 0x00);
}

void audio_disable_dac(void) {
    audio_write(0x14, 0xff);
    audio_write(0x12, audio_read(0x12) | 0x3b);
    audio_write(0x13, 0xff);
}

void audio_set_vol(uint8_t value) {
	if(value > 100) { value = 100; }
	// 音量寄存器: 0x00 ~ 0x3F 音量递减
	value = 0x3F - (0x3F * value / 100);
    audio_write(0x10, value);	// left channel
    audio_write(0x11, value);	// right channel
}

void audio_dump_regs(void) {
	dumpBin((const void*)I2S_BASE, 24, 16, true, false);
	dumpByte(audio_read(0), DumpByteFlagBegin, true, false);
	for(int i=1; i<=0x3F; i++) {
		dumpByte(audio_read(i), DumpByteFlagData, true, false);
	}
	dumpByte(audio_read(0x40), DumpByteFlagEnd, true, false);
}

// 填充一半的 TX-FIFO
static _RAMCODE_ void _feed_half_txfifo(void) {
	for(int i=0; i<I2S_FIFO_DEPTH/2; i++) { i2s_reg->data = 0; }
}

// 读取一半的 RX-FIFO
static _RAMCODE_ void _fetch_half_rxfifo(void) {
	volatile s16 data;
	for(int i=0; i<I2S_FIFO_DEPTH/2; i++) { data = i2s_reg->data; }
}

static _RAMCODE_ void _clear_rxfifo(void) {
	volatile s16 data;
	for(int i=0; i<I2S_FIFO_DEPTH; i++) { data = i2s_reg->data; }
}

void audio_config(const audio_reg_t *regs, audio_sample_rate_t rate, bool enableIrq, bool enableMic, bool enableSpeaker, uint8_t volume) {
	// 禁用 I2S 中断
	NVIC_DisableIRQ(I2S_IRQn);
	
	// 打开 codec 电源
	pmu_codec_power_enable();
	// 填写寄存器
	while(regs->addr != 0xFF) {
		audio_write(regs->addr, regs->value); regs++;
	}
	// 设置采样频率
	audio_hw_set_freq(rate);
	
	// 注意：必须先使能 speaker 再使能 mic，否则 mic 不工作！非常奇怪！
	if(enableSpeaker) {
		// 设置音量
		audio_set_vol(volume);
		// Speaker
		i2s_init(I2S_DIR_TX, rate, 1);
		audio_enable_dac();
		// 音频功放打开
		if(_funcPASwitch) { _funcPASwitch(1); }
	}
	
	// 注意：必须先使能 speaker 再使能 mic，否则 mic 不工作！非常奇怪！
	if(enableMic) {
		// MIC
		i2s_init(I2S_DIR_RX, rate, 1);
		audio_enable_adc();
	}
	
	// clear rx fifo
	_clear_rxfifo();
	
	// 使能 I2S
    i2s_reg->ctrl.en = 1;

	if(enableIrq) {
		// !!! 注意：一定要在最后使能 I2S 中断，否则可能会不停地中断导致其它代码无法执行 !!!
		i2s_reg->ctrl.rx_int_en = enableMic;
		i2s_reg->ctrl.tx_int_en = enableSpeaker;
		i2s_reg->ctrl.inten = 1;
		NVIC_EnableIRQ(I2S_IRQn);
	}
}

void audio_init(FUNC_PA_Switch funcPASwitch) {
	_funcPASwitch = funcPASwitch;
	_audio_mode = AUDIO_MODE_NONE;
	//audio_dump_regs();
}

void audio_set_pa_switch(FUNC_PA_Switch funcPASwitch) {
	_funcPASwitch = funcPASwitch;
}


//
// for Notes Play
//
/// 音节音乐播放时状态
static notes_play_runtime_t *_noteInfo = NULL;

/// 停止播放
static void _play_notes_stop(void) {
	if(_noteInfo) {
		// 调用播放完成回调函数
		if(_noteInfo->playCompleteCallback) { _noteInfo->playCompleteCallback(_noteInfo); }
		osTaskDestroy(_noteInfo->decodeTaskId);
		osFree(_noteInfo); _noteInfo = NULL; 
		logd("Notes music stop.\r\n");
	}
}

static std_err_t _play_notes_next(const Note *notes) {
	if(!_noteInfo) { return STD_ERR_INVALID_STATE; }
	
	// 如果指定了音节，则播放指定音节，否则播放下一个音节
	_noteInfo->note = note_validate(notes) ? notes : note_next(_noteInfo->note);
	// 如果遇到停止符，则表示播放完成
	if(_noteInfo->note == NULL) { return STD_ERR_EOF; }
	
	// 查表获取当前音节的频率
	int freq = ToneFreq[_noteInfo->note->tone] * _noteInfo->toneX / 10;
	// 查表获取当前音节的节拍时长(ms)
	int time = BeatTime[_noteInfo->note->beat] * 10 / _noteInfo->speedX;
	// 计算本音节总共需要播放多少个采样点
	_noteInfo->sampleCountTotal = time * AUDIO_NOTES_SAMPLE_RATE / 1000;
	// 计算本音节半个频率周期需要播放多少个采样点
	_noteInfo->sampleCountHalfCycle = IDIV45(AUDIO_NOTES_SAMPLE_RATE, freq);
	// 复位
	_noteInfo->sampleCountPlayed = 0;
	
	return STD_ERR_OK;
}

// 解码循环任务
static OSTaskDef(_decodeNotesLoop) {	
	// 播放完成，停止播放
	if(!_noteInfo) { audio_stop(); return; }
	
	s16 value;
	while(!i2s_reg->status.tx_full) {
		// 计算并发送采样点数据：如果位于偶数半周期内，则为正值；否则为负值
		value = _noteInfo->sampleCountPlayed / _noteInfo->sampleCountHalfCycle;
		value = (value & 0x01) ? -MAX_SAMPLE_VALUE : MAX_SAMPLE_VALUE;
		
		// 送出采样值
		i2s_reg->data = value;
		_noteInfo->sampleCountPlayed++;
		
		// 检查本音节是否播放完成
		if(_noteInfo->sampleCountPlayed >= _noteInfo->sampleCountTotal) {
			// 本音节播放完成，继续播放下一个音节
			if(_play_notes_next(NULL) != STD_ERR_OK) {
				// 所有音节播放完成，停止播放
				audio_stop(); return;
			}
		}
	}
}

std_err_t audio_play_notes(const Note *notes, audio_sample_rate_t sampleRate, uint8_t volume, uint8_t speedX, uint8_t toneX, uint8_t repeat, FUNC_play_notes_complete completeHandler) {
	if(!notes || !volume || !speedX || !toneX) { return STD_ERR_INVALID_ARG; }
	
	// 先复位状态
	audio_stop();
	
	// 分配播放所需的数据结构内存
	_noteInfo = osMalloc(sizeof(notes_play_runtime_t));
	if(!_noteInfo) { return STD_ERR_NO_MEM; }
	_noteInfo->volume = volume;
	_noteInfo->speedX = speedX;
	_noteInfo->toneX = toneX;
	_noteInfo->repeat = repeat;
	_noteInfo->playCompleteCallback = completeHandler;

	// 设置开始播放第一个音节
	_play_notes_next(notes);
	// 启动解码循环任务，解码出来第一个数据块之后才真正开始播放
	_noteInfo->decodeTaskId = osTaskRunRoutine(_decodeNotesLoop, 0, NULL);
	// 设置为播放模式
	_audio_mode = AUDIO_MODE_PLAY_NOTES;
	
	// 音频配置
	// 注意：播放时最好要禁用 I2S 中断，否则会不停地进中断，解码任务根本没有机会执行！
	audio_config(_regs, sampleRate, false, false, true, volume);
	
	logd("Start notes music play ...\r\n");
	return STD_ERR_OK;
}


//
// for Audio Play
//
// wave 运行时数据结构
static wave_runtime_t *_wave = NULL;

static void _play_wave_stop(void) {	
	// 释放内存
	if(_wave) {
		// 调用播放完成回调函数
		if(_wave->playCompleteCallback) { _wave->playCompleteCallback(_wave); }
		if(_wave->samplesQueue) { QChar_destroy(_wave->samplesQueue); }
		osTaskDestroy(_wave->decodeTaskId);
		osFree(_wave); _wave = NULL;
	}
	logd("Stop audio play.\r\n");
	//print_memory_usage();
}

// 解码循环任务
static OSTaskDef(_decodeWaveLoop) {	
    std_err_t err = STD_ERR_OK;
	s16 sampleValue = 0;
	int n = 0;
	
	// 如果没有要播放的内容，则停止播放
	if(!_wave) { audio_stop(); }
	
	// 如果已经播放完成，则停止播放
	if(_wave->playedSamples >= _wave->sampleCount && i2s_reg->status.tx_emtpy) {
		if(!_wave->repeat) {
			// 不需要重复播放了，播放完成
			audio_stop();
		} else {
			// 重复次数减1
			_wave->repeat--;
			// 重新开始播放
			_wave->currentBlock = 0;
			_wave->playedSamples = 0;
		}
		return;
	}
		
	// 逐个输出采样点到 I2S TX FIFO
    while(!i2s_reg->status.tx_full) {
		// 从队列中取出一个采样点
		n = QChar_popn(_wave->samplesQueue, (uint8_t*)&sampleValue, 2);
		if(n < 2) {
			// 队列为空，尝试解码
            err = wave_codec_decode(_wave);
			if(err && err != STD_ERR_CONTINUE) {
                // 解码失败！停止播放！
                audio_stop();
                return;
            }
			continue;
		}
		
		// 输出采样点到 I2S TX FIFO
		i2s_reg->data = le2h16(sampleValue);
		// 记录已经播放的采样点数量
		_wave->playedSamples++;
	}
	//logd("%d / %d\r\n", _wave->playedSamples, _wave->sampleCount);
}

std_err_t audio_play_wave(const void *data, uint32_t len, uint8_t volume, uint8_t repeat, FUNC_play_wave_complete completeHandler) {
	if(!data || !len || !volume) { return STD_ERR_INVALID_ARG; }
	
	// 先复位状态
	audio_stop();
	//print_memory_usage();
	
	// 分配播放运行时数据结构
	_wave = (wave_runtime_t*)osMalloc(sizeof(wave_runtime_t));
	if(!_wave) { return STD_ERR_NO_MEM; }
	memset(_wave, 0, sizeof(wave_runtime_t));
	
	// 解析音频文件
	std_err_t err = wave_codec_parse_file(data, len, _wave);
	if(err) { osFree(_wave); _wave = NULL; return err; }

	// 初始化播放运行时数据结构
	_wave->volume = volume;
	_wave->repeat = repeat;
	_wave->playedSamples = 0;
	_wave->samplesQueue = NULL;
	_wave->playCompleteCallback = completeHandler;
	
	// 启动解码循环任务，解码出来第一个数据块之后才真正开始播放
	_wave->decodeTaskId = osTaskRunRoutine(_decodeWaveLoop, 0, NULL);
	// 设置为播放模式
	_audio_mode = AUDIO_MODE_PLAY_WAVE;
	
	// 音频配置
	// 注意：播放时最好要禁用 I2S 中断，否则会不停地进中断，解码任务根本没有机会执行！
	audio_config(_regs, (audio_sample_rate_t)_wave->sampleRate, false, false, true, volume);
	
	logd("Start audio play ...\r\n");
	return STD_ERR_OK;
}


//
// for Audio Loopback
//
#define LOOPBACK_BUF_SIZE (I2S_FIFO_DEPTH/2)
static uint16_t *_loopbackBuf = NULL;
static uint16_t _loopbackDataLen = 0;

void _loopback_stop(void) {
	if(_loopbackBuf) { osFree(_loopbackBuf); _loopbackBuf = NULL; }
}

std_err_t audio_loopback_start(void) {
	// 先复位状态
	audio_stop();
	
	// 分配缓冲区
	_loopbackBuf = osMalloc(LOOPBACK_BUF_SIZE*2);
	if(!_loopbackBuf) { return STD_ERR_NO_MEM; }
	// 设置音频工作模式
	_audio_mode = AUDIO_MODE_LOOPBACK;
	// 音频配置
	audio_config(_regs, AUDIO_LOOPBACK_SAMPLE_RATE, true, true, true, 100);
	
	return STD_ERR_OK;
}

static _RAMCODE_ void i2s_isr_loopback(void) {
    // MIC
    if(i2s_reg->status.rx_half_full || i2s_reg->status.rx_full) {
		if(!_loopbackDataLen) {
			i2s_get_data(_loopbackBuf, LOOPBACK_BUF_SIZE, I2S_DATA_MONO);
			_loopbackDataLen = LOOPBACK_BUF_SIZE;
		} else {
			_fetch_half_rxfifo();
		}
    }
	
    // Speaker
    if(i2s_reg->status.tx_half_empty || i2s_reg->status.tx_emtpy) {
		if(_loopbackDataLen) {
			i2s_send_data(_loopbackBuf, LOOPBACK_BUF_SIZE, I2S_DATA_MONO);
			_loopbackDataLen = 0;
		} else {
			_feed_half_txfifo();
		}
    }
}


//
// for Audio Record
//
typedef struct {
	// 录音数据块处理回调函数
	FUNC_record_block_handler blockHandler;
	// 用于触发录音的电平(=0 表示不用触发，直接开始录音)
	uint16_t trigLevel;
	// 用于触发录音的电平(=0 表示不用触发，直接开始录音)
	uint16_t trigCountMax;
	// 触发计数值：当有声音采样值超过 _trigLevel 后加1，否则减1；直至超过 _trigCountMax 后开始录音
	uint16_t trigCount;
	// 录音数据块大小(一个块中有多少个采样点)
	uint16_t blockSize;
	// 采样点缓冲区中已经录了多少个采样点
	uint16_t sampleCount;
	// 采样点缓冲区
	s16 samples[1];
} audio_record_runtime_t;

static audio_record_runtime_t *_recordInfo = NULL;

// 停止录音并复位相关的状态
static void _record_stop() {
	if(_recordInfo) { osFree(_recordInfo); _recordInfo = NULL; }
}

std_err_t audio_record_start(FUNC_record_block_handler handler, uint16_t blockSize, uint16_t trigLevel, uint16_t trigCountMax) {
	if(!blockSize || !handler) { return STD_ERR_INVALID_ARG; }
	if(_audio_mode == AUDIO_MODE_RECORD) { return STD_ERR_OK; }
	
	// 状态复位
	audio_stop();
	// 设置音频工作模式
	_audio_mode = AUDIO_MODE_RECORD;
	
	// 分配缓冲区
	_recordInfo = osMalloc(sizeof(audio_record_runtime_t) + blockSize*2);
	if(!_recordInfo) { return STD_ERR_NO_MEM; }
	_recordInfo->trigLevel = trigLevel;
	_recordInfo->trigCountMax = trigCountMax;
	_recordInfo->trigCount = 0;
	_recordInfo->blockHandler = handler;
	_recordInfo->blockSize = blockSize;
	_recordInfo->sampleCount = 0;
	
	// 音频配置
	audio_config(_regs, AUDIO_RECORD_SAMPLE_RATE, true, true, false, 100);
	return STD_ERR_OK;
}

uint32_t audio_record_sync_detect_max(uint32_t skipTimeMs, uint32_t totalTimeMs) {
	// 状态复位
	audio_stop();
	// 设置音频工作模式
	_audio_mode = AUDIO_MODE_RECORD;
	// 音频配置(不启动 MIC 中断)
	audio_config(_regs, AUDIO_RECORD_SAMPLE_RATE, false, true, false, 100);

	// 准备循环采样
	uint32_t skipCount  = AUDIO_RECORD_SAMPLE_RATE * skipTimeMs  / 1000; // 要跳过的采样点数量
	uint32_t totalCount = AUDIO_RECORD_SAMPLE_RATE * totalTimeMs / 1000; // 总采样点数量
	uint32_t count = 0;
	uint32_t maxSample = 0;
	volatile s16 data;
	// 循环采样，并找到最大值
	while(count < totalCount) {
		if(i2s_reg->status.rx_empty) {
			osDelayUs(10);
		} else {
			data = i2s_reg->data; count++;
			if(count <= skipCount) { continue; }
			data = ABS(data);
			if(data > maxSample) { maxSample = data; }
		}
	}
	// 状态复位
	audio_stop();
	return maxSample;
}

uint32_t audio_record_sync_detect_avg(uint32_t skipTimeMs, uint32_t totalTimeMs) {
	// 状态复位
	audio_stop();
	// 设置音频工作模式
	_audio_mode = AUDIO_MODE_RECORD;
	// 音频配置(不启动 MIC 中断)
	audio_config(_regs, AUDIO_RECORD_SAMPLE_RATE, false, true, false, 100);

	// 准备循环采样
	uint32_t skipCount  = AUDIO_RECORD_SAMPLE_RATE * skipTimeMs  / 1000; // 要跳过的采样点数量
	uint32_t totalCount = AUDIO_RECORD_SAMPLE_RATE * totalTimeMs / 1000; // 总采样点数量
	uint32_t count = 0;
	uint32_t avgSample = 0;
	volatile s16 data;
	// 循环采样，并计算平均值
	while(count < totalCount) {
		if(i2s_reg->status.rx_empty) {
			osDelayUs(10);
		} else {
			data = i2s_reg->data; count++;
			if(count <= skipCount) { continue; }
			avgSample += ABS(data);
		}
	}
	// 状态复位
	audio_stop();
	return avgSample / (totalCount - skipCount);
}

static _RAMCODE_ void i2s_isr_record(void) {
    if(i2s_reg->status.rx_half_full || i2s_reg->status.rx_full) {
		if(_recordInfo->trigCountMax && _recordInfo->trigLevel && _recordInfo->trigCount < _recordInfo->trigCountMax) {
			// 需要触发录音，目前还未达到触发条件
			volatile s16 data;
			for(int i=0; i<I2S_FIFO_DEPTH/2; i++) {
				// 读取一个采样值
				data = i2s_reg->data;
				// 判断此采样值是否会触发开始录音
				if(ABS(data) >= _recordInfo->trigLevel) {
					if(++(_recordInfo->trigCount) >= _recordInfo->trigCountMax) { return; }
				} else {
					if(_recordInfo->trigCount > 0) { _recordInfo->trigCount--; }
				}
			}
			return;
		}
		
		// 计算本次要读取的采样点数量
		uint16_t count = _recordInfo->blockSize - _recordInfo->sampleCount;
		count = MIN(count, I2S_FIFO_DEPTH/2);
		if(count) {
			// 填写录音采样数据
			i2s_get_data(_recordInfo->samples + _recordInfo->sampleCount, count, I2S_DATA_MONO); _recordInfo->sampleCount += count;
		} else {
			// 缓冲区已经满了，当前的数据丢掉？！
			_fetch_half_rxfifo();
		}
		
		// 如果缓冲区已满，则调用回调函数
		if(_recordInfo->sampleCount >= _recordInfo->blockSize && _recordInfo->blockHandler) {
			// 调用回调函数，然后清空缓冲区
			bool needContinue = _recordInfo->blockHandler(_recordInfo->samples, _recordInfo->sampleCount); _recordInfo->sampleCount = 0;
			if(needContinue) {
				// 需要继续录音
			} else if(_recordInfo->trigCountMax && _recordInfo->trigLevel) {
				// 需重新触发后，再次自动录音
				_recordInfo->trigCount = 0;
			} else {
				// 停止录音
				audio_stop();
			}
		}
    }
}


//
// Common
//
_RAMCODE_ void i2s_isr_ram(void) {
	if(_audio_mode == AUDIO_MODE_LOOPBACK) { 
		i2s_isr_loopback();
	} else if(_audio_mode == AUDIO_MODE_RECORD) {
		i2s_isr_record();
	} else {
		// 复位状态
		audio_stop();
	}
}

void audio_stop(void) {
	if(_audio_mode == AUDIO_MODE_NONE) { return; }

	// 关闭 I2S 中断，否则可能会不停地产生中断
	NVIC_DisableIRQ(I2S_IRQn);
	i2s_reg->ctrl.en = 0;
	// 音频功放关闭
	if(_funcPASwitch) { _funcPASwitch(0); }
	// 停止 Speaker
	//audio_disable_dac();
	// 停止 MIC
	//audio_disable_adc();
	// FIXBUG: !!! 注意：要恢复寄存器到初始化状态，否则下次录音启动时会有800ms多的噪声 !!!
	for(int i=0; i<sizeof(_init_regs); i++) { audio_write(i, _init_regs[i]); }
	// 关闭 codec 电源
	pmu_codec_power_disable();
	
	// 各状态额外清理工作
	if(_audio_mode == AUDIO_MODE_PLAY_WAVE) {
		_play_wave_stop();
	} else if(_audio_mode == AUDIO_MODE_PLAY_NOTES) {
		_play_notes_stop();
	} else if(_audio_mode == AUDIO_MODE_LOOPBACK) {
		_loopback_stop();
	} else if(_audio_mode == AUDIO_MODE_RECORD) {
		_record_stop();
	}

	// 设置工作模式
	_audio_mode = AUDIO_MODE_NONE;
}

// FIXBUG: !!! 注意：当音频播放完成时，启动800ms的录音（跳过噪声），以便下次录音启动时避开噪声，快速采集到有效的数据 !!!
void audio_clear_record_noise(void) {
	audio_config(_regs, AUDIO_RECORD_SAMPLE_RATE, false, true, false, 100);
	osDelayMs(800);
}
